#include "bsp_ic.h"

/* 保存频率、周期 */
static float Period = 0;
static float Freq = 0;

void IC_Init(void)
{
    /* 开启时钟 */
    IC1_TIM_CLK_ENABLE();
    IC1_GPIO_CLK_ENABLE();
    TIM_InternalClockConfig(IC1_TIM);   // 使用内部时钟

    /* 初始化GPIO */
    GPIO_InitTypeDef GPIO_InitStruct = {0};
    GPIO_InitStruct.GPIO_Pin         = IC1_GPIO_PIN;
    GPIO_InitStruct.GPIO_Mode        = GPIO_Mode_IPU;
    GPIO_InitStruct.GPIO_Speed       = GPIO_Speed_50MHz;
    GPIO_Init(IC1_GPIO_PORT, &GPIO_InitStruct);

    /* 初始化时基单元 */
    TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct = {0};
    TIM_TimeBaseInitStruct.TIM_ClockDivision       = TIM_CKD_DIV1;
    TIM_TimeBaseInitStruct.TIM_CounterMode         = TIM_CounterMode_Up;
    TIM_TimeBaseInitStruct.TIM_Period              = IC1_TIM_Period;     // ARR
    TIM_TimeBaseInitStruct.TIM_Prescaler           = IC1_TIM_Prescaler;  // PSC
    TIM_TimeBaseInitStruct.TIM_RepetitionCounter   = 0;
    TIM_TimeBaseInit(IC1_TIM, &TIM_TimeBaseInitStruct);

    /* 初始化输入捕获单元 */
    TIM_ICInitTypeDef TIM_ICInitStruct = {0};
    TIM_ICInitStruct.TIM_Channel       = IC1_TIM_CH;
    TIM_ICInitStruct.TIM_ICFilter      = 0xf;
    TIM_ICInitStruct.TIM_ICPolarity    = TIM_ICPolarity_Rising; // 需要捕获的边沿
    TIM_ICInitStruct.TIM_ICPrescaler   = TIM_ICPSC_DIV1;
    TIM_ICInitStruct.TIM_ICSelection   = TIM_ICSelection_DirectTI;
    TIM_ICInit(IC1_TIM, &TIM_ICInitStruct);

    /* 配置PWM输入模式 */
    // TIM_PWMIConfig(IC1_TIM, &TIM_ICInitStruct);

    /* 选择从模式触发 */
    // TIM_SelectInputTrigger(IC1_TIM, TIM_TS_TI1FP1);
    // TIM_SelectSlaveMode(IC1_TIM, TIM_SlaveMode_Reset); // 从模式触发用于复位CNT
    // TIM_SelectMasterSlaveMode(IC1_TIM, TIM_MasterSlaveMode_Enable);
    /* 配置捕获中断 */
    TIM_ClearFlag(IC1_TIM, TIM_FLAG_CC1);
    TIM_ITConfig(IC1_TIM, TIM_IT_CC1, ENABLE);
    TIM_ClearFlag(IC1_TIM, TIM_FLAG_Update);// 更新中断，用来记录CNT溢出的次数
    TIM_ITConfig(IC1_TIM, TIM_IT_Update, ENABLE);

    /* 初始化NVIC */
    NVIC_InitTypeDef NVIC_InitStruct                  = {0};
    NVIC_InitStruct.NVIC_IRQChannel                   = TIM3_IRQn;
    NVIC_InitStruct.NVIC_IRQChannelCmd                = ENABLE;
    NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;
    NVIC_InitStruct.NVIC_IRQChannelSubPriority        = 1;
    NVIC_Init(&NVIC_InitStruct);

    /* 开启定时器 */
    TIM_Cmd(IC1_TIM, ENABLE);
}

/**
 * @brief 获取捕获到引脚电平跳变时CNT的值（测频法）
 * 
 * @param TIMx    定时器
 * @param IC_Num  输入捕获的通道
 * @return uint32_t 
 */
uint16_t IC_GetCapture(TIM_TypeDef* TIMx, uint8_t IC_Num)
{
    if (IC_Num == 1) return TIM_GetCapture1(TIMx);
    if (IC_Num == 2) return TIM_GetCapture2(TIMx);
    if (IC_Num == 3) return TIM_GetCapture3(TIMx);
    if (IC_Num == 4) return TIM_GetCapture4(TIMx);
}

/**
 * @brief 获取捕获信号的频率
 * 
 * @param TIMx 定时器
 * @param IC_Num 通道号
 * @return uint32_t 频率 = 72M/(PSC+1)/(CCR+1)
 */
uint32_t IC_GetFreq(TIM_TypeDef* TIMx, uint8_t IC_Num)
{
    uint32_t tmp = 72000000 / (IC1_TIM_Prescaler + 1);
    return (tmp / (IC_GetCapture(TIMx, IC_Num) + 1));
}

/**
 * @brief 获取捕获到的输入信号的频率（在中断中计算得到的值）
 * 
 * @return float 频率值
 */
float IC_GetFreq_IT(void)
{
    return Freq;
}

/**
 * @brief 获取捕获到的输入信号的周期（在中断中计算得到的值）
 * 
 * @return float 周期值
 */
float IC_GetPeriod_IT(void)
{
    return Period;
}

/**************************************** 中断回调函数 ****************************************/

void TIM3_IRQHandler()
{
    /* 计算第一次触发和第二次触发时的CNT差值，用总频率72M/(PSC+1)除以差值得到输入信号的频率(1/周期) */
    static uint8_t numCNT = 0, numUpdate = 0;// 捕获次序、ARR寄存器溢出次数
    if (TIM_GetITStatus(IC1_TIM, TIM_IT_CC1) == SET) { 
        static uint16_t firstCCR, secondCCR;
        if (!numCNT) {// 第一次捕获到上升沿时，保存CCR寄存器的值
            firstCCR = IC_GetCapture(IC1_TIM, 1);
            numCNT = 1;
        } else {
            secondCCR = IC_GetCapture(IC1_TIM, 1);
            // 输入频率 = 定时器频率/(CCR2-CCR1)
            Freq = (float)(secondCCR - firstCCR + numUpdate * IC1_TIM_Period);
            Freq = (float)(72000000.0/(IC1_TIM_Prescaler+1)) / (Freq);
            Period = 1/Freq;
            numUpdate = numCNT = 0;
        }
        TIM_ClearFlag(IC1_TIM, TIM_IT_CC1);
    } else if (TIM_GetITStatus(IC1_TIM, TIM_IT_Update) == SET) {// CNT溢出中断
        if (numCNT)// 只有第一次捕获到上升沿后才考虑溢出
            numUpdate++;
        // else
        //     TIM_ITConfig(IC1_TIM, TIM_IT_Update, DISABLE);
        TIM_ClearFlag(IC1_TIM, TIM_IT_Update);
    }
}